<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
  <title>vConsole: Log</title>
  <link href="./lib/weui.min.css" rel="stylesheet"/>
  <link href="./lib/demo.css" rel="stylesheet"/>

  <!-- <script src="../dist/vconsole.min.js"></script> -->
  <!-- <script src="https://unpkg.com/vconsole/dist/vconsole.min.js"></script> -->
</head>
<body ontouchstart>
  <div id="page" class="page">
    <div class="section">
    <div class="hd">String</div>

      <a onclick="simpleStrings()" href="javascript:;" class="weui_btn weui_btn_default">Simple Strings</a>
      <a onclick="printf()" href="javascript:;" class="weui_btn weui_btn_default">Printf</a>
      <a onclick="styling()" href="javascript:;" class="weui_btn weui_btn_default">Styling</a>
    </div>

    <div class="section">
      <div class="hd">Object</div>

      <a onclick="normalObject()" href="javascript:;" class="weui_btn weui_btn_default">Common Object</a>
      <a onclick="largeObject()" href="javascript:;" class="weui_btn weui_btn_default">Large Object</a>
      <a onclick="circularObject()" href="javascript:;" class="weui_btn weui_btn_default">Circular Object & Array</a>
      <a onclick="proxyObject()" href="javascript:;" class="weui_btn weui_btn_default">Proxy Object</a>
    </div>

    <div class="section">
      <div class="hd">Common Log</div>

      <a onclick="allDataTypes()" href="javascript:;" class="weui_btn weui_btn_default">All Data Types</a>
      <a onclick="logTypes()" href="javascript:;" class="weui_btn weui_btn_default">Log/Info/Debug/Warn/Error</a>
      <a onclick="differentPanelLog()" href="javascript:;" class="weui_btn weui_btn_default">Output to Different Panels</a>
      <a onclick="logTime()" href="javascript:;" class="weui_btn weui_btn_default">console.time</a>
      <a onclick="logGroup()" href="javascript:;" class="weui_btn weui_btn_default">console.group</a>
    </div>

    <div class="section">
      <div class="hd">Behavior</div>

      <a onclick="formattingLog()" href="javascript:;" class="weui_btn weui_btn_default">Formatting Logs</a>
      <a onclick="repeatLog()" href="javascript:;" class="weui_btn weui_btn_default">Repeated Logs</a>
      <a onclick="maxLogNumber()" href="javascript:;" class="weui_btn weui_btn_default">Reach maxLogNumber</a>
    </div>

    <div class="section">
      <div class="hd">Exception</div>

      <a onclick="windowError()" href="javascript:;" class="weui_btn weui_btn_default">window.error</a>
      <a onclick="promiseReject()" href="javascript:;" class="weui_btn weui_btn_default">Promise Reject</a>
      <a onclick="xssLog()" href="javascript:;" class="weui_btn weui_btn_default">XSS Log</a>
    </div>

    <div class="section">
      <div class="hd">Exporter</div>

      <a onclick="callExportLog('log')" href="javascript:;" class="weui_btn weui_btn_default">vConsole.log.log()</a>
      <a onclick="callExportLog('system')" href="javascript:;" class="weui_btn weui_btn_default">vConsole.system.log()</a>
      <a onclick="callExportClear()" href="javascript:;" class="weui_btn weui_btn_default">vConsole.log.clear()</a>
    </div>

    <div class="section">
      <div class="hd">Dev</div>

      <a onclick="observeMutation()" href="javascript:;" class="weui_btn weui_btn_default">Observe Mutation</a>
      <a onclick="scrolling()" href="javascript:;" class="weui_btn weui_btn_default">Scrolling</a>
      <a onclick="showTimestamps()" href="javascript:;" class="weui_btn weui_btn_default">showTimestamps</a>
    </div>
  </div>
</body>
</html>

<script type="module">
import '../dist/vconsole.min.js';

window.vConsole = new window.VConsole({
  log: { maxLogNumber: 25, showTimestamps: false },
  // target: '#page',
  // target: document.getElementById('page'),
  // disableLogScrolling: true,
  // theme: 'dark',
  // defaultPlugins: ['system'],
  onReady: function() {
    console.log('vConsole: onReady');
  },
});
</script>

<script>
// setInterval(() => {
//   console.log(Math.random());
//   console.log('[system]', Math.random());
// }, 300);

// make a prototype chain
class FooBaseBaseClass {

}
class FooBaseClass extends FooBaseBaseClass {
  constructor() {
    super();
    this.name = 'base';
  }
}
class FooClass extends FooBaseClass {
  constructor() {
    super();
    this.bar = 'foo';
  }
}

// make a global object
var aa = {
  ba: {
    ca: 1,
    cb: 2,
    cc: 3,
  },
  bb: {
    cd: 4,
    ce: 5,
  },
  bc: [1, 2, 3],
};

function simpleStrings() {
  console.log('Hello vConsole!');
  console.log(`Line 1,
  Line 2`);
}

function printf() {
  console.log('Hi %s, Im %s', 'Foo', 'Bar'); // Hi Foo, Im Bar
  console.log('I had %d cakes', 3); // I had 3 cakes
  console.log('The %o is large', aa); // The [[aa]] is large
}

function styling() {
  console.log('%c blue %c red', 'color:blue', 'color:red'); // blue red
  console.log('%c FOO', 'font-weight:bold', 'bar'); // FOO bar
  console.log('%c Foo %c bar', 'color:red'); // Foo %c bar
}

function differentPanelLog() {
  console.log('[default]', 'This log should be shown in Log panel.');
  console.log('[DEFAULT]', 'Switch to System tab to see next log.');
  console.log('[system]', 'This log should be shown in System panel.');
  console.log('[foobar]', 'This log should be shown in Log panel. (No "foobar" plugin.)');
}

function logTime() {
  vConsole.show();
  const label = 'ab';
  console.time(label);
  for (let i = 0; i < 10; i++) {
    console.log('Wait...', i);
  }
  console.timeEnd(label);
  console.timeEnd(label);
}

function logGroup() {
  vConsole.show();
  console.log('This is the outer level');
  console.group();
    console.log('Level 2');
    console.group(aa);
      console.log('Level 3');
      console.groupCollapsed('LV4');
        console.warn('More of level 4');
        console.info(aa);
      console.groupEnd();
      console.log('Back to level 3')
    console.groupEnd();
    console.log('Back to level 2');
  console.groupEnd();
  console.log('Back to the outer level');
}

function formattingLog() {
  console.log('%c red,%c blue, %c bold.', 'color:red', 'color:blue', 'font-weight:bold', '(Use %c format)');
  console.log('%c %c Continuous styling, only blue is applied.', 'color:red', 'color:blue', '(Use %c format)');
  console.log('Invalid styling format %c will be %c ignored.', {color:'blue'}, '"/>xss', 'font-weight:bold', '(Use %c format)');
  console.log('Hi %s, Im %s!', 'Foo', 'Bar', '(Use %s format)');
  console.log('Force an object, %s, to be output as a string!', new FooClass, '(Use %s format)');
  console.log('Today is the %d day of this week.', (new Date).getDay() + 1, '(Use %d format)');
  console.log('Inline an object, %o, in a string.', new FooClass, '(Use %o format)');
}

function normalObject() {
  const obj = {
    number: 233,
    string: 'Hi you!\nFoo\t<b>bar!!</b>',
    boolean: true,
    symbolValue: Symbol('foo'),
    bigint: 98979695949n,
    obj: {foo: 'bar'},
    array: [8, 7, 6],
    undefined: undefined,
    func: function foo(a) { alert('b'); },
    class: FooClass,
    classInstance: new FooClass(),
  };
  obj[Symbol('symbolKey')] = 'The key is a symbol.';
  console.log('A common object:', obj);
  // console.log(obj.classInstance);
  // console.log(obj.classInstance.__proto__);
  // console.log(Object.getPrototypeOf(obj.classInstance));
}

function circularObject() {
  class NestObj { }
  const nest = new NestObj();
  nest.self = nest;
  const obj = {
    foo: 'bar',
    nest: nest,
  };
  obj.self = obj;
  obj.next = {};
  obj.next.next = obj.next;
  obj.next.self = obj;
  console.log('A cyclically referenced object:', obj);

  const arr = [];
  arr[0] = 'foo';
  arr[1] = arr;
  console.log('A cyclically referenced array:', arr);
}

function largeObject() {
  const obj = {
    window: window,
    longList: [],
  };
  for (let i = 0; i < 300; i++) {
    obj.longList.push(i + 1);
  }
  console.log('largeObject', obj);
  // setInterval(() => { console.log(Math.random()); }, 100);
}

function proxyObject() {
  const obj = {
    // foo: 'bar',
  };
  let warnCount = 0;
  const p = new Proxy(obj, {
    ownKeys(target) {
      const keys = Reflect.ownKeys(target);
      if (warnCount < 10000) {
        warnCount++;
        // window._vcOrigConsole.warn('Access to a proxy object\'s keys.', warnCount);
        console.warn('Access to a proxy object\'s keys.');
      }
      return keys;
      // return [];
    },
  }); 
  console.log('A proxy object:', p); 
}

function allDataTypes(aa, bb, cc) {
  console.log('String:', 'The first line;\nThe second line.', '<b>A HTML tag line.</b>');
  console.log('Number:', 65535);
  console.log('BigInt:', 99987654321n);
  console.log('Boolean:', true, false);
  console.log('Null:', null);
  console.log('Undefined:', undefined);
  console.log('Symbol:', Symbol('Foo'));
  console.log('Array:', [0,1,2,3,4,5,6,7,8,9,10,11]);
  console.log('Object:', {'foo': 'bar'});
  console.log('Function:', allDataTypes);
  console.log('Class:', FooClass);
  console.log('ClassInstance:', new FooClass);
}

function logTypes() {
  console.log('console.log()');
  console.info('console.info()');
  console.debug('console.debug()');
  console.warn('console.warn()');
  console.error('console.error()');
}

function repeatLog() {
  console.log('repeatLog() Start');
  const a = {a: {b: 1}};
  const e = new Error();
  const f = function(){};
  let count = 0;
  let timer = setInterval(() => {
    count ++;
    console.log('repeat', 'foo bar', f);
    if (count > 10) {
      clearInterval(timer);

      console.warn('repeat warn', a);
      console.warn('repeat warn', a);
      console.warn('repeat warn', a);
      console.error('repeat error', e);
      console.error('repeat error', e);
      console.log('repeatLog() End');
    }
  }, 50);
}

function maxLogNumber() {
  let n = 0;
  setInterval(() => {
    n++;
    console.log('Log:', n);
  }, 100);
}

function windowError() {
  console.log(new Error('Hello Error!'));
  a.b = 1;
}

function promiseReject() {
  Promise.reject('reject a string');
  Promise.reject(new Error('reject an Error'));
  Promise.reject({message: 'reject an object'});
  Promise.reject(666).then((result) => {
    console.log('resolved', result);
  }, (result) => {
    console.log('rejected', result);
  });
}

function xssLog() {
  const arr = [
    "-->\tyo\n'\"/><iframe>",
    ["-->\tyo\n'\"/><iframe>"]
  ];
  const obj = {
    'key': 'foo\nbar\tyoo',
    "-->\tyo\n'\"/><div>": "-->\n'\"/><iframe>"
  };
  console.log('XSS str:', "-->\tyo\n'\"/><iframe><i>");
  console.log(arr);
  console.log(obj);
}

function callExportLog(name) {
  vConsole[name].log(`The following logs`, `will not output to browser's console.\n`, `vConsole.${name}.log(...)`);
  vConsole[name].info(`vConsole.${name}.info(...)`);
  vConsole[name].debug(`vConsole.${name}.debug(...)`);
  vConsole[name].warn(`vConsole.${name}.warn(...)`);
  vConsole[name].error(`vConsole.${name}.error(...)`);
  vConsole.showPlugin(name === 'log' ? 'default' : name);
  vConsole.show();
}

function callExportClear() {
  vConsole.log.clear();
  vConsole.showPlugin('default');
  vConsole.show();
}

let observerStarted = false;
const observer = new MutationObserver((mutations) => {
  for (let i = 0; i < mutations.length; i++) {
    const mutation = mutations[i];
    switch (mutation.type) {
      case 'childList':
        window._vcOrigConsole.log(mutation.type, 'add:'+mutation.addedNodes.length, 'remove:'+mutation.removedNodes.length, mutation.target);
        break;
      case 'characterData':
        window._vcOrigConsole.log(mutation.type, 'old:'+mutation.oldValue, mutation.target);
        break;
      case 'attributes':
        let newValue = mutation.target.attributes[mutation.attributeName]?.value;
        window._vcOrigConsole.log(mutation.type, 'name:'+mutation.attributeName, 'old:'+mutation.oldValue, 'new:'+newValue, mutation.target);
        break;
    }
  }
});
function observeMutation() {
  if (observerStarted === false) {
    observerStarted = true;
    observer.observe(document.documentElement, {
      attributes: true,
      childList: true,
      characterData: true,
      subtree: true
    });
  } else {
    observerStarted = false;
    observer.disconnect();
  }
}

function scrolling() {
  const m = 100;
  vConsole.setOption('log.maxLogNumber', m);
  let n = 0
  let t = setInterval(() => {
    n++;
    if (n > m) {
      vConsole.setOption('log.maxLogNumber', 25);
    }
    if (n > m + 10) {
      clearInterval(t);
    }
    console.log('scrolling', n);
  }, 150);
}

let showTime = false;
function showTimestamps() {
  showTime = !showTime;
  vConsole.setOption('log.showTimestamps', showTime);
  vConsole.show();
}
</script>